package com.mp.approvalreq.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mp.approvalreq.entity.*;
import com.plusmoney.exception.RpcServerLogicalException;
import com.plusmoney.util.Utils;
import com.plusmoney.util.ValidateUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author liuqi
 * @date 2018/6/8
 */
public class ConditionFlowUtils {

    private static final Logger logger = LoggerFactory.getLogger(ConditionFlowUtils.class);


    /**
     * 条件审批流筛选
     * 条件计算类型: -- 范围(暂时没用)
     * -- 比较
     * -- 金额
     * -- 新版报销
     * -- 其它模板开启了成本分析
     * -- 时长
     * -- 单位: 小时  -- 请假、补假、外出、出差自定义模板
     * -- 单位: 天    -- 请假、补假、外出、出差、自定义模板
     */
    public static String chooseConditionFlow(Request request, List<ConditionFlow> conditionFlowList,
                                             Map<Integer, TemplateComponent> componentMap, Template template) throws ParseException {
        int templateType = template.getDefaultType();
        logger.info("审批类型:" + templateType);
        ConditionFlow conFlow = conditionFlowList.get(0);
        logger.info("conditionFlowList:{}", JSONObject.toJSONString(conditionFlowList));
        switch (conFlow.getConditionCalculationType()) {
            case ConditionCalculationType.TO_COMPARE:
                switch (Integer.parseInt(conFlow.getConditionType())) {
                    case ConditionType.MONEY:
                        //  金额
                        return chooseMoneyConditionFlow(request, conditionFlowList, componentMap, template);
                    case ConditionType.TIME_LAST:
                        // 时长
                        return chooseTimeConditionFlow(request, conditionFlowList, componentMap, template);
                    default:
                        throw new RpcServerLogicalException(1, "错误的条件审批流");
                }
            case ConditionCalculationType.RADIO_BUTTON:
                return chooseRadioButtonFlow(request, conditionFlowList, template);
            default:
                throw new RpcServerLogicalException(1, "错误的条件审批流");
        }
    }


    /**
     * 单选项条件审批流
     *
     * @param request
     * @param conditionFlowList
     * @param template
     * @return
     */
    private static String chooseRadioButtonFlow(Request request, List<ConditionFlow> conditionFlowList, Template template) {
        int num = Integer.parseInt(conditionFlowList.get(0).getRelaComponentNum());
        boolean isInput = false;

        for (TemplateComponent component : request.getComponentsContent()) {
            for (ConditionFlow conditionFlow : conditionFlowList) {
                if (component.getNum() == num) {
                    isInput = true;
                    List<String> items = JSONArray.parseArray(conditionFlow.getChooseItems(), String.class);
                    if (StringUtils.isEmpty(component.getValue())) {
                        throw new RpcServerLogicalException(1, component.getName() + "为审批流判断条件，不能为空");
                    }
                    if (items.contains(component.getValue())) {
                        return conditionFlow.getRequestFlow();
                    }
                }
            }
        }

        // 条件审批流的选项不能为空
        if (!isInput) {
            List<TemplateComponent> tempComList = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
            throw new RpcServerLogicalException(1, getErrorName(tempComList, String.valueOf(num)));
        }

        logger.info("没有匹配的选项:{}", template.getTemplateId());
        return RequestFlowType.DEFAULT;
    }


    private static String chooseTimeConditionFlow(Request request, List<ConditionFlow> conditionFlowList,
                                                  Map<Integer, TemplateComponent> componentMap, Template template) throws ParseException {
        int templateType = template.getDefaultType();
        // 请假、补假
        if (templateType == Template.LEAVE || templateType == Template.COMPENSATORY_LEAVE) {
            checkTimeParam(request);
            Long seconds = getLeaveSeconds(request, templateType);
            for (ConditionFlow conditionFlow : conditionFlowList) {
                BigDecimal value;
                if (conditionFlow.getUnitType() == 1) {
                    // 小时制
                    transferHourToSecond(conditionFlow);
                    value = new BigDecimal(seconds);
                } else {
                    // 天制
                    value = getDays(request.getNaturalContentTimeLast());
                }
                if (isConditionMatched(conditionFlow, value)) {
                    return conditionFlow.getRequestFlow();
                }
            }
        } else if (templateType == Template.DAYS_OFF) {
            // 调休时长
            DaysOffTimeInfo daysOffTimeInfo = ApprovalUtils.getDaysOffTimeInfo(componentMap);
            if (StringUtils.isEmpty(daysOffTimeInfo.getLeaveTimestamp())) {
                throw new RpcServerLogicalException(1, "调休时长参数不能为空");
            }
            for (ConditionFlow conditionFlow : conditionFlowList) {
                if (conditionFlow.getUnitType() == 1) {
                    // 小时制
                    transferHourToSecond(conditionFlow);
                    BigDecimal value = new BigDecimal(daysOffTimeInfo.getLeaveTimestamp());
                    if (isConditionMatched(conditionFlow, value)) {
                        return conditionFlow.getRequestFlow();
                    }
                }
            }
        } else {
            List<TemplateComponent> components = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
            String[] componentNums = getRelatedComponentNums(conditionFlowList);

            String timeOne = getValueFromTcMap(componentMap, componentNums[0], components);
            String timeTwo = getValueFromTcMap(componentMap, componentNums[1], components);

            checkTime(timeOne, componentNums[0], timeTwo, componentNums[1], components);

            Long seconds = getDiffSeconds(timeOne, timeTwo);
            logger.info(String.format("时间1:%s, 时间2:%s, 相差多少秒:%s", timeOne, timeTwo, seconds));

            for (ConditionFlow conditionFlow : conditionFlowList) {
                // 小时制
                BigDecimal value;
                if (conditionFlow.getUnitType() == 1) {
                    transferHourToSecond(conditionFlow);
                    value = new BigDecimal(seconds);
                } else {
                    // 天制
                    value = new BigDecimal(seconds / (24 * 3600));
                }
                if (isConditionMatched(conditionFlow, value)) {
                    return conditionFlow.getRequestFlow();
                }
            }
        }
        throw new RpcServerLogicalException(1, "时间格式错误，无法应用条件审批流");
    }

    private static Long getLeaveSeconds(Request request, int templateType) throws ParseException {
        List<String> times = ApprovalUtils.getTimeList(request.getComponentsContent(), templateType);
        logger.info("考勤计算的时长:" + request.getRequestContentLastTotalSecond());
        if (StringUtils.isEmpty(request.getRequestContentLastTotalSecond())) {
            return getDiffSeconds(times.get(0), times.get(1));
        } else {
            return Long.valueOf(request.getRequestContentLastTotalSecond());
        }
    }

    private static void transferHourToSecond(ConditionFlow conditionFlow) {
        BigDecimal min = conditionFlow.getSmallerFactor();
        BigDecimal max = conditionFlow.getGreaterFactor();
        conditionFlow.setSmallerFactor(min.multiply(new BigDecimal(3600)));
        conditionFlow.setGreaterFactor(Utils.isNull(max) ? null : max.multiply(new BigDecimal(3600)));
    }


    private static String[] getRelatedComponentNums(List<ConditionFlow> conditionFlowList) {
        ConditionFlow conFlow = conditionFlowList.get(0);

        // 时间条件的审批流关联的是两个组件num
        String[] relatedNumArr = conFlow.getRelaComponentNum().split(",");
        if (relatedNumArr.length != 2) {
            logger.error(conFlow.getRelaComponentNum());
            throw new RpcServerLogicalException(1, "条件审批流设置项错误");
        }

        return relatedNumArr;
    }

    private static void checkTime(String timeOne, String num1, String timeTwo, String num2, List<TemplateComponent> components) {
        logger.info("出发、返回时间: " + timeOne + "  -- " + timeTwo);

        if (StringUtils.isEmpty(timeOne)) {
            String errorName = getErrorName(components, num1);
            throw new RpcServerLogicalException(1, errorName);
        }

        if (StringUtils.isEmpty(timeTwo)) {
            String errorName = getErrorName(components, num2);
            throw new RpcServerLogicalException(1, errorName);
        }
    }


    /**
     * 选择金额条件审批流
     *
     * @param request
     * @param conditionFlowList
     * @param componentMap
     * @param template
     * @return
     */
    private static String chooseMoneyConditionFlow(Request request, List<ConditionFlow> conditionFlowList,
                                                   Map<Integer, TemplateComponent> componentMap, Template template) {
        try {
            int templateType = template.getDefaultType();
            // 新版报销才有该字段
            boolean isNewExpenseVersion = request.getTotalMoney() != null;

            if (templateType == Template.EXPENSES && isNewExpenseVersion) {
                BigDecimal value = new BigDecimal(request.getTotalMoney());
                logger.info("金额:" + value.toString());

                for (ConditionFlow conditionFlow : conditionFlowList) {
                    if (isConditionMatched(conditionFlow, value)) {
                        return conditionFlow.getRequestFlow();
                    }
                }
            } else {
                List<TemplateComponent> tempComList = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);

                ConditionFlow flow = conditionFlowList.get(0);
                String value = getValueFromTcMap(componentMap, flow.getRelaComponentNum(), tempComList);
                logger.info("金额:" + value);
                BigDecimal money = new BigDecimal(value);

                for (ConditionFlow conditionFlow : conditionFlowList) {
                    if (isConditionMatched(conditionFlow, money)) {
                        return conditionFlow.getRequestFlow();
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }

        throw new RpcServerLogicalException(1, "金额格式错误，无法应用条件审批流");
    }


    /**
     * 检验请假、补假的条件审批流参数
     *
     * @param req
     */
    private static void checkTimeParam(Request req) {
        if (StringUtils.isEmpty(req.getNaturalContentTimeLast())
                || StringUtils.isEmpty(req.getRequestContentLastTotalSecond())) {
            throw new RpcServerLogicalException(1, "时长计算失败，请稍候再试 ");
        }
    }


    /**
     * 根据条件审批流关联的组件num,从前端提交的审批组件Map中获取值,如果值为null,则报: xxx不能为空
     *
     * @param componentMap
     * @param num
     * @param tempComList
     * @return
     */
    private static String getValueFromTcMap(Map<Integer, TemplateComponent> componentMap, String num, List<TemplateComponent> tempComList) {
        String value;
        try {
            value = componentMap.get(Integer.valueOf(num)).getValue();
        } catch (Throwable e) {
            throw new RpcServerLogicalException(1, getErrorName(tempComList, num));
        }

        if (StringUtils.isEmpty(value)) {
            throw new RpcServerLogicalException(1, getErrorName(tempComList, num));
        }

        return value;
    }


    /**
     * 获取对应的组件名
     *
     * @param tempComList
     * @param num
     * @return
     */
    private static String getErrorName(List<TemplateComponent> tempComList, String num) {
        try {
            for (TemplateComponent tempCom : tempComList) {
                if (Integer.parseInt(num) == tempCom.getNum()) {
                    return tempCom.getName() + "为审批流判断条件，不能为空";
                }
            }
        } catch (Throwable e) {
            logger.error("条件审批选项不能为空", e);
            return "条件审批选项不能为空";
        }

        return "条件审批选项不能为空";
    }


    /**
     * 获取两个时间段相差的秒数
     *
     * @param timeOne
     * @param timeTwo
     * @return
     * @throws ParseException
     */
    private static Long getDiffSeconds(String timeOne, String timeTwo) throws ParseException {
        SimpleDateFormat sdf = DateUtils.getSimpleDateFormat(DateUtils.YMDHM);
        Date dateOne = sdf.parse(timeOne);
        Date dateTwo = sdf.parse(timeTwo);
        long seconds = (dateOne.getTime() - dateTwo.getTime()) / (1000);
        return Math.abs(seconds);
    }


    /**
     * 是否匹配该项金额条件
     *
     * @param conditionFlow
     * @param value
     * @return
     */
    private static boolean isConditionMatched(ConditionFlow conditionFlow, BigDecimal value) {
        BigDecimal min = conditionFlow.getSmallerFactor();
        BigDecimal max = conditionFlow.getGreaterFactor();

        logger.info("min:{},max:{},value:{},left:{},right:{}", min, max, value, conditionFlow.getLeftSymbol(), conditionFlow.getRightSymbol());
        if (Utils.isNull(max)) {
            return compare(min, value, conditionFlow.getLeftSymbol());
        }
        if (isZero(value) && isZero(min)) {
            return true;
        }
        return compare(min, value, conditionFlow.getLeftSymbol()) && compare(value, max, conditionFlow.getRightSymbol());
    }

    private static boolean isZero(BigDecimal value) {
        return value.equals(BigDecimal.ZERO);
    }

    private static boolean compare(BigDecimal left, BigDecimal right, int symbol) {
        if (symbol == ConditionFlow.SYMBOL_LESS_THAN_OR_EQUAL) {
            return left.compareTo(right) <= 0;
        }
        return left.compareTo(right) < 0;
    }


    private static BigDecimal getDays(String natureDay) {
        BigDecimal days;
        logger.info("natureDay:{}", natureDay);
        if (!natureDay.contains("天")) {
            // 不够1天算1天
            days = new BigDecimal(1);
        } else {
            String day = natureDay.split("天")[0];
            if (ValidateUtil.isInteger(day)) {
                int countDay = Integer.parseInt(day);
                if (natureDay.contains("小时") || natureDay.contains("分钟")) {
                    // 存在分钟或小时,则算 [countDay.1天]
                    days = new BigDecimal((countDay + ".1"));
                } else {
                    days = new BigDecimal(countDay);
                }
            } else {
                days = new BigDecimal(day);
            }
        }
        return days;
    }


}
